#-*- coding: utf-8 -*-

# (BSD 2-clause)
# Copyright (c) 2011, Mario Hock, Michael Tänzer, Karin Schmidt.
# All rights reserved.
# 
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 
# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE OF TELEMATICS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ARIBA PROJECT OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation
# are those of the authors and should not be interpreted as representing
# official policies, either expressed or implied, of the copyright holders.

import gtk
import xml.etree.ElementTree as etree
from os import path
import omnibus_steeringwheel as steeringwheel


class Chat(object):
    _chats = dict()
    
    # only to be initialized once
    log_tag_table = None
    
    @classmethod
    def open_chat(cls, connection, user_name):
        '''
        opens the chat window or brings it into the foreground and returns the
        associated Chat object
        '''
        if user_name in Chat._chats:
            Chat._chats[user_name].present()
        else:
            Chat._chats[user_name] = cls(connection, user_name)
        
        return Chat._chats[user_name]
    
    
    @classmethod
    def chat_msg_handler(cls, connection, _, **kwargs):
        cls.open_chat(connection, kwargs['sender']).message_received(**kwargs)
    
    @classmethod
    def chat_echo_handler(cls, connection, _, **kwargs):
        cls.open_chat(connection, kwargs['receiver']).message_sent(**kwargs)
    
    @classmethod
    def chat_ack_handler(cls, connection, _, **kwargs):
        cls.open_chat(connection, kwargs['chat_to']).message_acked(**kwargs)
    
    @classmethod
    def register_event_handlers(cls, connection):
        connection.register_event_handler(cls.chat_msg_handler,
                                          steeringwheel.EVENT_TYPE.CHAT_MSG)
        connection.register_event_handler(cls.chat_echo_handler,
                                          steeringwheel.EVENT_TYPE.CHAT_ECHO)
        connection.register_event_handler(cls.chat_ack_handler,
                                          steeringwheel.EVENT_TYPE.CHAT_ACK)
    
    
    def __init__(self, connection, user_name):
        self._connection = connection
        
        dir = path.dirname(__file__)
        
        self.builder = gtk.Builder()
        self.builder.add_from_file(
                path.join(dir, "chat-window.glade"))
        self.builder.connect_signals(self)
        
        if Chat.log_tag_table is None:
            log_tag_table = gtk.TextTagTable()
            log_tag_table.add(self.builder.get_object('other_base_style'))
            log_tag_table.add(self.builder.get_object('my_base_style'))
            log_tag_table.add(self.builder.get_object('my_acked_style'))
            log_tag_table.add(self.builder.get_object('time_stamp_style'))
            log_tag_table.add(self.builder.get_object('user_name_style'))
            log_tag_table.add(self.builder.get_object('message_style'))
            Chat.log_tag_table = log_tag_table
        
        buffer = gtk.TextBuffer(Chat.log_tag_table)
        buffer.insert(buffer.get_end_iter(), 'Chat with '+user_name+' started')
        self.builder.get_object('log_text').set_buffer(buffer)
        
        self.user_name = user_name
        self.builder.get_object('user_name').set_label(user_name)
        
        self._window = self.builder.get_object('chat_window')
        self._window.set_title(u'%s - Omnibus Chat' % user_name)
        self._window.show()
        
        self.set_focus_on_message_input()
    
    def set_focus_on_message_input(self):
        input = self.builder.get_object('message_input')
        self._window.set_focus(input)
    
    def insert_message(self,
                       sender,
                       message_id,
                       time_stamp,
                       message,
                       isEcho = False,
                       **_):
        
        if isEcho:
            base_style = 'my_base_style'
        else:
            base_style = 'other_base_style'
        
        
        log_scroll = self.builder.get_object('log_scroll')
        # do we need to auto-scroll later? (i.e. are we at the bottom?)
        adjustment = log_scroll.get_vadjustment()
        if adjustment.value >= adjustment.upper - adjustment.page_size - 1e-12:
            # the 1e-12 is because floating point calculations are not exact
            auto_scroll = True
        else:
            auto_scroll = False
        
        log_text = self.builder.get_object('log_text')
        log_buffer = log_text.get_buffer()
        
        log_buffer.insert(log_buffer.get_end_iter(), "\n")
        
        log_buffer.create_mark("begin_"+message_id,
                                log_buffer.get_end_iter(),
                                left_gravity = True)
        
        log_buffer.insert_with_tags_by_name(log_buffer.get_end_iter(),
                 '('+time_stamp+') ', base_style, 'time_stamp_style')
        log_buffer.insert_with_tags_by_name(log_buffer.get_end_iter(),
                 sender+': ', base_style, 'user_name_style')
        log_buffer.insert_with_tags_by_name(log_buffer.get_end_iter(),
                 message, base_style, 'message_style')
        
        end_mark = log_buffer.create_mark("end_"+message_id,
                                log_buffer.get_end_iter(),
                                left_gravity = True)
        
        if auto_scroll:
            log_text.scroll_mark_onscreen(end_mark)
    
    def message_entered(self):
        message_input = self.builder.get_object('message_input')
        message = unicode(message_input.get_text(), 'utf-8')
        
        message_input.set_text("")
        #message_input.select_region(0, -1)
        
        self._connection.chat(self.user_name, message)
    
    def message_received(self, **kwargs):
        self.insert_message(**kwargs)
        
        if not self._window.is_active():
            self._window.set_urgency_hint(True)
            self._window.set_urgency_hint(False)
    
    def message_sent(self, **kwargs):
        self.insert_message(isEcho=True, **kwargs)
    
    def message_acked(self, message_id, **_):
        log_buffer = self.builder.get_object('log_text').get_buffer()
        begin = log_buffer.get_iter_at_mark(
                log_buffer.get_mark("begin_"+message_id)
                )
        end = log_buffer.get_iter_at_mark(
                log_buffer.get_mark("end_"+message_id)
                )
        
        log_buffer.apply_tag_by_name("my_acked_style", begin, end)
    
    def present(self):
        self._window.present()
    
    
    #===========================================================================
    # action handlers
    #===========================================================================
    def on_message_input_activate(self, *_):
        self.message_entered()
    
    def on_send_button_clicked(self, *_):
        self.message_entered()
        self.set_focus_on_message_input()
        
    def on_chat_window_delete_event(self, *_):
        del Chat._chats[self.user_name]
        return False
        
